package com.jy.api.utils;

import com.jy.api.shiro.token.ApiToken;
import com.jy.framework.exception.MyException;
import com.jy.framework.sys.entity.SysUserEntity;
import com.jy.framework.utils.RSAUtils;
import com.jy.framework.utils.RedisUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

/**
 * jwt工具类
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2017/9/21 22:21
 */
@ConfigurationProperties(prefix = "jy.jwt")
@Component
public class JwtUtils {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private String secret;
    private long expire;
    private String header;
    private String sysUserEntity;

    private ConcurrentHashMap<Long,Long> tokenVersion = new ConcurrentHashMap<>();

    @Autowired(required = false)
    private RedisUtils redisUtils;
    @Value("${jy.redis.open}")
    private boolean redisOpen;
    @Value("${jy.shiro.redis}")
    private boolean shiroRedis;
    /**
     * 生成jwt token
     */
//    public String generateToken(SysUserEntity sysUserEntity, String password) {
//
//        Date nowDate = new Date();
//        //过期时间
//        long date = nowDate.getTime() + expire * 1000;
//        Date expireDate = new Date(date);
//        Long userEntityId = sysUserEntity.getId();
//
//        long version = resetVersion(userEntityId,date);
//
//        String sub = userEntityId + "," +sysUserEntity.getUsername() + "," + password+ "," +sysUserEntity.getUserType() + "," +sysUserEntity.getPassword()+ "," +version ;
//        String encrypt = RSAUtils.encrypt(sub);
//        return Jwts.builder()
//                .setHeaderParam("typ", "JWT")
//                .setSubject(encrypt)
//                .setIssuedAt(nowDate)
//                .setExpiration(expireDate)
//                .signWith(SignatureAlgorithm.HS512, secret)
//                .compact();
//    }
    public String generateToken(SysUserEntity sysUserEntity, String password) {
        sysUserEntity.setPassword(password);
        Date nowDate = new Date();
        //过期时间
        long date = nowDate.getTime() + expire * 1000;
        Date expireDate = new Date(date);

//       生成 session
        Subject subject = SecurityUtils.getSubject();
        String sessionId = (String) subject.getSession().getId();

        password="";
        String sub = sysUserEntity.getId() + "," +sysUserEntity.getUsername() + "," + password+ "," +sysUserEntity.getUserType() + "," +sysUserEntity.getPassword()+ "," + -1+ "," + sessionId ;
        String encrypt = RSAUtils.encrypt(sub);
        String token = Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(encrypt)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();

        ApiToken apiToken = new ApiToken(sysUserEntity,token);
        subject.login(apiToken);
        return token;
    }

    /**
     * 处理缓存 version
     * @param userEntityId
     * @param expireDate
     * @return
     */
    private long resetVersion(Long userEntityId, long expireDate) {
        long version = System.nanoTime();
        if (redisOpen&&shiroRedis){
            redisUtils.set("version:"+ userEntityId,version,expireDate);
        }else {
            tokenVersion.put(userEntityId, version);
        }
        return version;
    }

    public Long getUserIdByToken(String token) {
            String decrypt = getBody(token);
            return Long.valueOf(decrypt.split(",")[0]);
    }
    public String getUsernameByToken(String token) {
            String decrypt = getBody(token);
            return decrypt.split(",")[1];
    }
    public ApiToken buildApiToken(String token) {
            String decrypt = getBody(token);
            String[] split = decrypt.split(",");
            SysUserEntity sysUserEntity = new SysUserEntity();
            sysUserEntity.setId(split[0]);
            sysUserEntity.setUsername(split[1]);
            sysUserEntity.setPassword(split[2]);
            sysUserEntity.setUserType(Integer.valueOf(split[3]));
            ApiToken apiToken = new ApiToken(sysUserEntity, token);
            return apiToken;
    }

    public String getSessionId(String token) {
            String decrypt = getBody(token);
            String[] split = decrypt.split(",");
            return split[6];
    }

    private String getBody(String token) {
        Claims body = getClaims(token);
        String subject = body.getSubject();
        String decrypt = RSAUtils.decrypt(subject);
        String[] split = decrypt.split(",");
        String s = split[0];
        Long time = Long.parseLong(split[5]);
        Long userId = Long.parseLong(s);

//        checkVersion(time, userId);
        return decrypt;
    }

    /**
     * 校验 version
     * @param time
     * @param userId
     */
    private void checkVersion(Long time, Long userId) {
        Long version=0L;
        if (redisOpen&&shiroRedis){
            version = Long.valueOf(redisUtils.get("version:"+ userId));
        }else {
            version = tokenVersion.get(userId);
        }

        if (!time .equals(version)){
            throw new MyException("token 过期！");
        }
    }

    private Claims getClaims(String token) {
        return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
    }

    /**
     * token是否过期
     * @return  true：过期
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public long getExpire() {
        return expire;
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }

    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

    public String getSysUserEntity() {
        return sysUserEntity;
    }

    public void setSysUserEntity(String sysUserEntity) {
        this.sysUserEntity = sysUserEntity;
    }

}
